Vitalens
\n \n \n \n
map
\n \n \n \n ''' \n \n \n map_file.write(html_content)\n map_file.seek(0)\n \n # Read the HTML content and escape it\n html_file = map_file.read()\n escaped_html = html.escape(html_file)\n \n iframe = f''\n \n \n return iframe\n\n\n\n\n\nm = folium.Map(\n location=[52.28, 6.7], zoom_start=10,\n tiles="Cartodb Positron"\n)\n\n\nicon_path = "./Assets/Water Icon.png"\nicon = folium.CustomIcon(\n icon_path,\n icon_size=(30, 30),\n )\n \n\ndef calculate_centroid(coordinates):\n """\n Calculate the centroid of a polygon.\n\n Args:\n coordinates (list): List of coordinates defining the polygon.\n\n Returns:\n tuple: Coordinates of the centroid (latitude, longitude).\n """\n polygon = Polygon(coordinates)\n return polygon.centroid.y, polygon.centroid.x\n\ndef update_layers(wellsLayer=active_wells_df, industryLayer=industrial):\n """\n Update the layers on the map.\n\n Returns:\n folium.Map: Updated Folium map.\n """\n global m\n \n folium.TileLayer("OpenStreetMap",\n show=False).add_to(m)\n \n popup_well = folium.GeoJsonPopup(\n fields=["Name", "Balance area", "Value"],\n aliases=["Naam Put", "Balansgebied", "Onttrekking in Mm\xb3/jr"],\n )\n\n popup_hex = folium.GeoJsonPopup(\n fields=["cityName", "Water Demand", "Population 2022"],\n aliases=["Stadsnaam", "Watervraag in Mm\xb3/jr", "Bevolking - 2022"]\n )\n\n popup_industrial = folium.GeoJsonPopup(\n fields=["Place", "Licensed", "Current_Extraction_2019"],\n aliases=["Locatie", "Vergunde Onttrekking in Mm\xb3/jr", "Huidige Onttrekking in Mm\xb3/jr"]\n )\n\n \n colormap = branca.colormap.StepColormap(\n ["#caf0f8", "#90e0ef", "#00b4d8", "#0077b6", "#03045e"],\n vmin=round(hexagons_filterd["Water Demand"].quantile(0.0),1),\n vmax=round(cities_clean["Water Demand"].quantile(1),1),\n caption="Totale watervraag in Mm\\u00b3/yr",\n )\n \n active = wellsLayer[wellsLayer["Active"] == True]\n \n folium.GeoJson(\n active,\n name="Winningputten",\n zoom_on_click=True,\n popup=popup_well,\n tooltip=folium.GeoJsonTooltip(fields=["Name"], aliases=["Well Name:"]),\n marker=folium.Marker(\n icon=folium.Icon(\n icon_color="#f3f3f3", icon="arrow-up-from-ground-water", prefix="fa", color='cadetblue'\n )\n ),\n ).add_to(m)\n \n folium.GeoJson(\n industryLayer,\n name="Industri\xeble Waterwinning",\n zoom_on_click=True,\n popup=popup_industrial,\n tooltip=folium.GeoJsonTooltip(fields=["Place"], aliases=["Place:"]),\n marker=folium.Marker(\n icon=folium.Icon(\n icon_color="#d9534f", icon="industry", prefix="fa", color='lightred'\n )\n ),\n\n ).add_to(m)\n \n hex_layer = folium.GeoJson(\n cities_clean,\n name="Stadsvraag",\n style_function=lambda x: {\n "fillColor": (\n colormap(x["properties"]["Water Demand"])\n if x["properties"]["Water Demand"] is not None\n else "transparent"\n ),\n "color": (\n "darkgray"\n if x["properties"]["cityName"] is not None\n else "transparent"\n ),\n "fillOpacity": 0.8,\n "weight": 0.7,\n },\n popup=popup_hex,\n ).add_to(m)\n\n folium.GeoJson(\n main_pipes,\n name="Hoofdleidingen",\n style_function=lambda x: { \n "color": "#E27D79",\n "weight": (4 if x["properties"]["Diameter_mm"] > 350\n else (2 if x["properties"]["Diameter_mm"] > 250\n else 1)),\n "Opacity": 0.6,\n },\n show=False\n ).add_to(m)\n\n folium.GeoJson(\n hexagons_filterd,\n name="Natura2000 Beperkt Gebied",\n style_function=lambda x: {\n "fillColor": (\n "darkgreen"\n if x["properties"]["Type"] == "Restricted Natura2000"\n else "transparent"\n ),\n "color": (\n "darkgray"\n if x["properties"]["Balance Area"] is not None\n else "transparent"\n ),\n "fillOpacity": 0.8,\n "weight": 0.7,\n }, \n show=False,\n ).add_to(m)\n\n folium.GeoJson(\n hexagons_filterd,\n name="Beperkt Natuurnetwerk Nederland",\n style_function=lambda x: {\n "fillColor": (\n "#CAFAA2"\n if x["properties"]["Type"] == "Restricted Other"\n else "transparent"\n ),\n "color": (\n "darkgray"\n if x["properties"]["Balance Area"] is not None\n else "transparent"\n ),\n "fillOpacity": 0.8,\n "weight": 0.7,\n },\n show=False,\n ).add_to(m)\n \n folium.GeoJson(\n balance_areas,\n name="Balansgebieden",\n style_function=lambda x: {\n "fillColor": "transparent",\n "color": "#93419F",\n "weight": 3\n },\n show=True,\n tooltip=folium.GeoJsonTooltip(fields=['Balance Area'], labels=True)\n ).add_to(m)\n \n BA_4326 = balance_areas.to_crs(4326)\n BA_4326["centroid"] = BA_4326.centroid\n \n for _, r in BA_4326.iterrows():\n lat = r["centroid"].y\n lon = r["centroid"].x\n name = r["Balance Area"]\n print(lat,lon)\n folium.Marker(\n location=[lat, lon],\n icon=DivIcon(\n icon_size=(150,36),\n icon_anchor=(0,0),\n html='
{name}
'.format(name=name),)\n ).add_to(m)\n\n folium.LayerControl(position='topleft', autoZIndex=True).add_to(m)\n \n industryIcon = '''\n var marker = L.AwesomeMarkers.icon({\n icon_color="#d9534f", icon="industry", prefix="fa", color='lightred'\n )});\n '''\n \n # Use custom CSS to move the colormap legend to the bottom-right corner\n legend_html = '''\n \n \n \n\n \n \n \n
\n \n
\n Legenda\n
    \n
  • Waterwinlocaties
  • \n
  • Industri\xeble Waterwinlocaties
  • \n
  • {colormap}
  • \n
  • Leidingen <250mm \n 250mm - 350mm\n >400mm
  • \n
  • Natura200 Beschermd Gebied
  • \n
  • Beperkt Natuurnetwerk Nederland Gebied
  • \n
  • Balansgebied
  • \n\n
\n
\n
\n '''.format(colormap=colormap._repr_html_(), industryIcon=industryIcon)\n\n \n \n # Add the custom legend to the map\n m.get_root().html.add_child(folium.Element(legend_html))\n\n return m\n\n\n\n# Logarithmic function\ndef log_func(x, a, b):\n return a * np.log(x) + b\n\n\n# Function to estimate extent for a specific well\ndef estimate_Damage_for_well(type, well_name, target_percentage):\n # Find the row corresponding to the given well\n well_row = type[type['Name'] == well_name]\n \n if well_row.empty:\n print(f"Well '{well_name}' not found in the dataset.")\n pass\n else:\n # Extract the available percentage columns (non-NaN values)\n well_row = well_row.iloc[0] # Select the first row as Series\n perc_columns = well_row.dropna().index[1:] # Exclude the 'Name' column\n perc_values = [float(col) for col in perc_columns]\n extents = well_row[perc_columns].values\n \n if len(perc_values) < 2:\n return 0\n \n # Fit a logarithmic curve to the data\n try:\n popt, _ = curve_fit(log_func, perc_values, extents)\n # Use the fitted curve to predict the extent at the target percentage\n estimated_extent = log_func(target_percentage, *popt) \n return estimated_extent\n except Exception as e:\n print(f"Error in fitting the curve: {e}")\n return 0\n \n \n\nactive_scenarios = set()\ntext = ["## Scenario"]\n\n\ndef update_scenarioTitle(new_title):\n global text\n base_title = "Status - 2022"\n\n # Convert tuple to a list for modification\n text = list(text)\n # Update for main population scenarios\n if Scenario_Button.value == "Bevolking 2035":\n if "Versnelde Groei" in text:\n text.remove("Versnelde Groei")\n if base_title in text:\n text.remove(base_title)\n if new_title not in text:\n text.append(new_title)\n elif Scenario_Button.value == "Bevolking 2035 +1% toename":\n if "Autonome Groei" in text:\n text.remove("Autonome Groei")\n if base_title in text:\n text.remove(base_title)\n if new_title not in text:\n text.append(new_title)\n elif Scenario_Button.value == "Bevolking - 2022":\n if "Versnelde Groei" in text:\n text.remove("Versnelde Groei")\n if "Autonome Groei" in text:\n text.remove("Autonome Groei")\n if Scenario_Button.value not in text:\n if new_title not in text:\n text.append(new_title)\n\n # Update for small business scenarios\n if ScenarioSmall_Button.value == "Kleine Bedrijven +10% Vraag":\n if "Kleine Bedrijven Versnelde Groei" in text:\n text.remove("Kleine Bedrijven Versnelde Groei")\n if base_title in text:\n text.remove(base_title)\n if new_title not in text:\n text.append(new_title)\n elif ScenarioSmall_Button.value == "Kleine Bedrijven +35% Vraag":\n if "Kleine Bedrijven Autonome Groei" in text:\n text.remove("Kleine Bedrijven Autonome Groei")\n if base_title in text:\n text.remove(base_title)\n if new_title not in text:\n text.append(new_title)\n elif ScenarioSmall_Button.value == "Status - 2022":\n if "Kleine Bedrijven Versnelde Groei" in text:\n text.remove("Kleine Bedrijven Versnelde Groei")\n if "Kleine Bedrijven Autonome Groei" in text:\n text.remove("Kleine Bedrijven Autonome Groei")\n if Scenario_Button.value not in text:\n if new_title not in text:\n text.append(new_title)\n \n # Convert the list back to a tuple for immutability\n text = tuple(text)\n \n # Update the app title with the updated text list\n app_title.object = " - ".join(text)\n print(text)\n\n\n\ndef update_title(event):\n global text\n text =list(text)\n if ButtonSmallWells.value:\n if "Kleine Putten Gesloten" in text:\n print("Tekst is er al")\n else: \n text.append("Kleine Putten Gesloten")\n Measure1On()\n if ButtonSmallWells.value == False:\n Measure1Off()\n if "Kleine Putten Gesloten" in text:\n text.remove("Kleine Putten Gesloten")\n else:\n print("Tekst is er niet")\n if ButtonCloseNatura.value:\n if "Natura Putten Gesloten" in text:\n print("Tekst is er al")\n else: \n text.append("Natura Putten Gesloten")\n Measure2On()\n if ButtonCloseNatura.value == False: \n Measure2Off()\n if "Natura Putten Gesloten" in text:\n text.remove("Natura Putten Gesloten")\n else:\n print("Tekst is er niet")\n if ButtonSmartMeter.value:\n if "Gebruik van Slimme Meters" in text:\n print("Tekst is er al")\n else: \n text.append("Gebruik van Slimme Meters")\n Measure3On()\n if ButtonSmartMeter.value == False: \n Measure3Off()\n if "Gebruik van Slimme Meters" in text:\n text.remove("Gebruik van Slimme Meters") \n else:\n print("Tekst is er niet")\n if ButtonImportWater.value:\n if "Water Importeren" in text:\n print("Tekst is er al")\n else: \n text.append("Water Importeren")\n Measure4On()\n if ButtonImportWater.value == False: \n Measure4Off()\n if "Water Importeren" in text:\n text.remove("Water Importeren") \n else:\n print("Tekst is er niet")\n if ButtonAddExtraIndustrial.value:\n if "Gebruik industri\xeble overcapaciteit" in text:\n print("Tekst is er al")\n else:\n text.append("Gebruik industri\xeble overcapaciteit")\n Measure5On()\n if ButtonAddExtraIndustrial.value == False:\n Measure5Off()\n if "Gebruik industri\xeble overcapaciteit" in text:\n text.remove("Gebruik industri\xeble overcapaciteit")\n else:\n print("Tekst is er niet")\n \n text = tuple(text)\n \n app_title.object = " - ".join(text)\n print(text)\n update_indicators()\n\n \n\n\ndef ScenarioBase():\n """\n Voer het basisscenario uit met een vraag gelijk aan het jaar 2022.\n\n Args:\n event: Het gebeurtenisobject.\n """\n global demand_capita\n hexagons_filterd["Current Pop"] = hexagons_filterd["Pop2022"]\n hexagons_filterd["Water Demand"] = (\n hexagons_filterd["Current Pop"] * demand_capita * smallBusiness * 365 \n ) / 1e6\n update_scenarioTitle("Bevolking - 2022")\n print("Basisscenario hersteld")\n update_indicators()\n\ndef Scenario1():\n """\n Voer het eerste scenario uit met een toename van de vraag.\n\n Args:\n event: Het gebeurtenisobject.\n """\n global demand_capita \n hexagons_filterd["Current Pop"] = hexagons_filterd["Pop2022"] * 1.0209\n\n hexagons_filterd["Water Demand"] = (\n hexagons_filterd["Current Pop"] * demand_capita * smallBusiness * 365 \n ) / 1e6\n update_scenarioTitle("Autonome Groei")\n print("Scenario 1 succesvol uitgevoerd")\n update_indicators()\n\ndef Scenario2():\n """\n Voer het tweede scenario uit met een vraagtoename van 2,09%.\n \n Args:\n event: Het gebeurtenisobject.\n """\n hexagons_filterd["Current Pop"] = hexagons_filterd["Pop2022"] * 1.0309\n\n hexagons_filterd["Water Demand"] = (\n hexagons_filterd["Current Pop"] * demand_capita * smallBusiness * 365\n ) / 1e6\n \n update_scenarioTitle("Versnelde Groei")\n print("Scenario 2 succesvol uitgevoerd")\n update_indicators()\n\n \ndef ScenarioSmallBusinessBase():\n global smallBusiness\n global demand_capita \n smallBusiness = 1.2\n update_scenarioTitle("Kleine Bedrijven Status 2022")\n update_indicators()\n\ndef ScenarioSmallBusiness1():\n global smallBusiness\n global demand_capita \n smallBusiness = 1.2*1.1\n update_scenarioTitle("Kleine Bedrijven Autonome Groei")\n update_indicators()\n\ndef ScenarioSmallBusiness2():\n global smallBusiness\n global demand_capita \n smallBusiness = 1.2*1.35\n update_scenarioTitle("Kleine Bedrijven Versnelde Groei")\n update_indicators()\n\n\ndef Measure1On():\n # Update the 'Active' column where 'Max_permit' is less than 5.00\n condition = active_wells_df["Max_permit"] < 5.00\n active_wells_df.loc[condition, "Active"] = False\n \n # Uncheck checkboxes corresponding to the wells that meet the condition\n for well_name in active_wells_df.loc[condition, "Name"]:\n checkboxes[well_name].value = False\n\n\ndef Measure1Off():\n # Update the 'Active' column where 'Max_permit' is less than 5.00\n condition = active_wells_df["Max_permit"] < 5.00\n active_wells_df.loc[condition, "Active"] = True\n \n # Uncheck checkboxes corresponding to the wells that meet the condition\n for well_name in active_wells_df.loc[condition, "Name"]:\n try:\n checkboxes[well_name].value = True\n except: continue\n\n\ndef Measure2On():\n """\n Activate the second measure (closing Natura 2000 wells).\n """\n active_wells_df.loc[active_wells_df["Name"] == "Archemerberg", "Active"] = False\n active_wells_df.loc[active_wells_df["Name"] == "Nijverdal", "Active"] = False\n \n # Update the checkboxes to reflect the new state\n checkboxes["Archemerberg"].value = False\n checkboxes["Nijverdal"].value = False\n\ndef Measure2Off():\n """\n Deactivate the second measure (closing Natura 2000 wells).\n """\n active_wells_df.loc[active_wells_df["Name"] == "Archemerberg", "Active"] = True\n active_wells_df.loc[active_wells_df["Name"] == "Nijverdal", "Active"] = True\n \n # Update the checkboxes to reflect the new state\n checkboxes["Archemerberg"].value = True\n checkboxes["Nijverdal"].value = True\n \ndef Measure3On():\n """\n Activate the third measure (using smart meters).\n """\n global demand_capita\n demand_capita = ButtonDemand.value/1000 * 0.95\n hexagons_filterd["Water Demand"] = (\n hexagons_filterd["Current Pop"] * demand_capita * smallBusiness * 365\n ) / 1e6\n\ndef Measure3Off():\n """\n Deactivate the third measure (using smart meters).\n """\n global demand_capita\n demand_capita = ButtonDemand.value/1000\n hexagons_filterd["Water Demand"] = (\n hexagons_filterd["Current Pop"] * demand_capita * smallBusiness * 365\n ) / 1e6\n \nfrom shapely.geometry import Point\nimport geopandas as gpd\nimport folium\n\ndef Measure4On():\n """\n Activate the fourth measure (importing water).\n """\n # Define the geometry point for the new well.\n new_geometry = Point(253802.6, 498734.2) # Projected coordinates\n\n # Add a new row using .loc by assigning to a new index (e.g., 'Imports')\n active_wells_df.loc[active_wells_df.index.max() + 1] = {\n "Name": "Imports",\n "Num_Wells": 3,\n "Ownership": 0,\n "Max_permit": 4.5,\n "Balance area": "Imported",\n "Active": True,\n "Current Value": 4.38,\n "Value": 4.38,\n "OPEX_m3": 0.0598173515981735,\n "Drought_m3": 0,\n "CO2_m3": 0,\n "Env_m3": 0,\n "envCost": 0,\n "OPEX": 0.262,\n "geometry": new_geometry\n }\n\n # Select the newly added well for visualization\n new_well = active_wells_df.loc[active_wells_df["Name"] == 'Imports']\n\n # Convert the selected well into a GeoDataFrame and create the GeoJSON representation.\n new_well_gdf = gpd.GeoDataFrame(new_well, geometry='geometry')\n new_well_gdf = new_well_gdf.to_json()\n\n # Add the new well as a GeoJson object on the map\n folium.GeoJson(\n new_well_gdf,\n name="Import Water",\n zoom_on_click=True,\n tooltip=folium.GeoJsonTooltip(fields=["Name"], aliases=["Well Name:"]),\n marker=folium.Marker(\n icon=folium.Icon(\n icon_color="#f3f3f3", icon="arrow-up-from-ground-water", prefix="fa", color='cadetblue'\n )\n ),\n show=True\n ).add_to(m)\n\n \n\ndef Measure4Off():\n """\n Deactivate the fourth measure (importing water).\n """\n try: \n # Use .loc to identify rows where 'Name' is 'Imports' and drop them\n active_wells_df.drop(active_wells_df.loc[active_wells_df["Name"] == 'Imports'].index, inplace=True) \n except KeyError:\n print("Row does not exist")\n\ndef Measure5On():\n global industrialExcess\n industrialExcess = industrial["Licensed"].sum()-industrial["Current_Extraction_2019"].sum()\n \ndef Measure5Off():\n global industrialExcess\n industrialExcess = 0 \n\n \n \ndef Reset(event):\n """\n Reset the application to its initial state.\n\n Args:\n event: The event object.\n """\n global demand_capita\n global smallBusiness\n demand_capita = 0.135\n smallBusiness = 1.2\n hexagons_filterd["Current Pop"] = hexagons_filterd["Pop2022"]\n hexagons_filterd["Water Demand"] = (\n hexagons_filterd["Current Pop"] * demand_capita * smallBusiness * 365\n ) / 1e6\n global active_wells_df\n active_wells_df = gpd.GeoDataFrame(\n {\n "Name": wells["Name"],\n "Num_Wells": wells["Num_Wells"],\n "Ownership": wells["Inside_Prop"],\n "Max_permit": wells["Permit__Mm3_per_jr_"],\n "Balance area": wells["Balansgebied"],\n "Active": [True] * len(wells),\n "Current Extraction" : wells["Extraction_2023__Mm3_per_jr_"],\n "Value": wells["Extraction_2023__Mm3_per_jr_"],\n "OPEX_m3": wells["totOpex_m3"],\n "Drought_m3": wells["DroughtDamage_EUR_m3"],\n "CO2_m3": wells["CO2Cost_EUR_m3"],\n "Env_m3": wells["env_cost_m3"],\n "envCost": wells["env_cost_m3"]\n * wells["Extraction_2023__Mm3_per_jr_"]\n * 1e6,\n "OPEX": wells["totOpex_m3"] * wells["Extraction_2023__Mm3_per_jr_"] * 1e6,\n "CAPEX": 0,\n "geometry": wells["geometry"],\n }\n)\n Scenario_Button.value = 'Bevolking - 2022'\n ScenarioSmall_Button.value = 'Status - 2022'\n ButtonDemand.value = 135\n ButtonSmallWells.value, ButtonCloseNatura.value, ButtonImportWater.value, ButtonSmartMeter.value = False, False, False, False\n update_scenarioTitle("Status - 2022")\n update_indicators()\n\ndef update_indicators(arg=None):\n total_extraction.value = calculate_total_extraction()\n total_opex.value = calculate_total_OPEX()\n total_capex.value = calculate_total_CAPEX()\n excess_cap.value = calculate_available()\n natureMidDamage_value.value=calculate_affected_Sensitive_Nature()\n natureHighDamage_value.value=calculate_affected_VerySensitive_Nature()\n # own_pane.value = calculate_ownership()\n co2_pane.value= calculate_total_CO2_cost()\n drought_pane.value = calculate_total_Drought_cost()\n # update_balance_opex()\n # update_balance_lzh_gauges()\n total_demand.value = calculate_total_Demand()\n total_difference.value = total_extraction.value - total_demand.value\n lzh.value = calculate_lzh()\n \n\n\n# Initialize a dictionary to hold the active state and slider references\nactive_wells = {}\n\n# Initialize a dictionary to hold the balance area layouts\nbalance_area_buttons = {}\n\n# Initialize a dictionary to hold the sliders\ncheckboxes = {}\n\n# Setup Well Radio Buttons\nRadio_buttons = []\nWell_radioB = []\noptions = ["-15% van Huidige", "Huidige", "85% van Max. Vergunning", "Maximale Vergunning", "115% van Max. Vergunning"]\n\n\n\nfor index, row in wells.iterrows():\n wellName = row["Name"]\n current_value = row["Extraction_2023__Mm3_per_jr_"]\n maxValue = row["Permit__Mm3_per_jr_"]\n balance_area = row["Balansgebied"]\n radio_group = pn.widgets.RadioButtonGroup(\n name=wellName,\n options=options,\n button_type='success',\n value="Huidige",\n orientation = "vertical"\n )\n \n # Add Checkbox and listeners\n checkbox = pn.widgets.Switch(name="Active", value=True, max_width=20)\n checkbox.param.watch(partial(toggle_well, well_name=wellName), "value")\n radio_group.param.watch(partial(update_radio, well_name=wellName), "value")\n \n # Store the checkbox in the dictionary for later updates\n checkboxes[wellName] = checkbox\n \n NameP = pn.pane.Str(wellName + f"\\nHuidige operatie op {(current_value/maxValue)*100:0.2f}%", styles={\n 'font-size': "14px",\n 'font-family': "Barlow",\n 'font-weight': 'bold',\n })\n\n \n Wellvalue = update_well_Value(wellName)\n well_style=styleWellValue(Wellvalue,maxValue)\n \n extractionPerWell = pn.pane.HTML(object=update_well_Value_formatted(wellName), styles=well_style)\n NameState = pn.Row(NameP, checkbox)\n Well_radioB = pn.Column(NameState, extractionPerWell, radio_group, styles=miniBox_style)\n \n # Add the well layout to the appropriate balance area layout\n if balance_area not in balance_area_buttons:\n balance_area_buttons[balance_area] = []\n balance_area_buttons[balance_area].append(Well_radioB)\n \n # Store the active state and radio group reference along with the NamePane\n active_wells[wellName] = {"active": True, "value": current_value, "radio_group": radio_group, "name_pane": extractionPerWell}\n\n \nall_wellsButton = pn.widgets.RadioButtonGroup(\n name="All Wells",\n options=options,\n button_type='success',\n value="Huidige",\n orientation = "vertical"\n )\nall_wellsButton.param.watch(update_allRadio,"value")\n \n \n# Maak HTML-tekst voor Wells-tabblad\nbalance_area_Text = pn.pane.HTML('''\n

Balansgebieden


\n

Hoofdcontrole: Met deze optie kunt u de putten tegelijkertijd bedienen. Anders kunt u ook put voor put bedienen via het uitklapmenu.

\n '''\n , width=300, align="start")\n\n# Maak een lay-out voor de radioknoppen\nradioButton_layout = pn.Accordion(styles={'width': '95%', 'color':'#151931'})\nfor balance_area, layouts in balance_area_buttons.items():\n balance_area_column = pn.Column(*layouts)\n radioButton_layout.append((balance_area, balance_area_column))\n\n \n\n \nScenario_Button = pn.widgets.RadioButtonGroup(name="Maatregelenknop Groep", options=['Bevolking - 2022', 'Bevolking 2035', 'Bevolking 2035 +1% toename'], button_type='warning', styles={\n 'width': '93%', 'border': '3px' }, orientation='vertical'\n )\nScenario_Button.param.watch(update_scenarios, "value")\n\nScenarioSmall_Button = pn.widgets.RadioButtonGroup(name="Maatregelenknop Groep", options=['Status - 2022', 'Kleine Bedrijven +10% Vraag', 'Kleine Bedrijven +35% Vraag'], button_type='warning', styles={\n 'width': '93%', 'border': '3px' }, orientation='vertical'\n )\nScenarioSmall_Button.param.watch(update_scenariosSmall, "value")\n\n\n# Button1 = pn.widgets.Button(\n# name='Autonomous growth', button_type="primary", width=300, margin=10,\n# )\n# Button1.param.watch(update_title, 'value')\n# Button1.on_click(Scenario1)\n\n# Button2 = pn.widgets.Button(\n# name="Accelerated growth", button_type="primary", width=300, margin=10, \n# )\n# Button2.param.watch(update_title, 'value')\n# Button2.on_click(Scenario2)\n\nButtonSmallWells = pn.widgets.Toggle(\n name='Sluit Kleine Putten', button_type="primary", button_style="outline", width=300, margin=10, \n)\nButtonSmallWells.param.watch(update_title, 'value')\n\nButtonCloseNatura = pn.widgets.Toggle(\n name='Sluit Natura 2000 Putten', button_type="primary", button_style="outline", width=300, margin=10, \n)\nButtonCloseNatura.param.watch(update_title, 'value')\n\nButtonDemand = pn.widgets.RadioButtonGroup(name='Waterbehoefte per Hoofd van de Bevolking', options=[135,120,100,90], button_type='warning',\n width=80, orientation='horizontal', styles={\n 'width': '97%', 'flex-wrap': 'no-wrap' }, align=("center", "center"))\nButtonDemand.param.watch(current_demand, 'value')\n\n# Button5= pn.Row(ButtonDemand, align=("center", "center"))\n\nButtonImportWater = pn.widgets.Toggle(\n name='Importeer Water', button_type="primary", button_style="outline", width=300, margin=10)\nButtonImportWater.param.watch(update_title, 'value')\n\nButtonAddExtraIndustrial = pn.widgets.Toggle(name="Voeg Industrieel Water Toe", button_type="primary", button_style="outline", width=300, margin=10,)\nButtonAddExtraIndustrial.param.watch(update_title, 'value')\n\nButtonSmartMeter = pn.widgets.Toggle(name="Gebruik Slimme Meters", button_type='primary', button_style='outline', width=300, margin=10)\nButtonSmartMeter.param.watch(update_title, 'value')\n\nButtonReset = pn.widgets.Button(\n name='Reset', button_type='danger', width=300, margin=10\n)\nButtonReset.on_click(Reset)\n\n\n# textYears = pn.pane.HTML(\n# '''\n#

Year Selection


\n# ''', width=300, align="start", styles={"margin": "5px"}\n# )\n\ntextDivider3 = pn.pane.HTML('''

Scenario's Kleine Bedrijven "Small Business include bakeries, hair saloons, retail stores, shopping malls, etc."


''')\n\ntextScenarioPop = pn.pane.HTML(\n '''\n

Scenario's Bevolking


'''\n , width=300, align="start"\n)\n\ntextB2 = pn.pane.HTML(\n '''Scenario met een vraagtoename van 35% ↴''', width=300, align="start"\n)\n\ntextMeasureSupp = pn.pane.HTML(\n '''

Aanvoermaatregelen


\n Sluit alle putlocaties met een productie van minder dan 5 Mm\\u00b3/jr ↴''', width=300, align="start", styles={}\n)\n\ntextCloseNatura = pn.pane.HTML(\n '''\n Sluit alle putlocaties binnen 100m van een Natura 2000-gebied ↴''', width=300, align="start", styles={}\n)\n\ntextMeasureDemand = pn.pane.HTML(\n '''

Vraagmaatregelen


\n Waterverbruik per hoofd van de bevolking in L/d''', width=300, align="start", styles={}\n)\n\ntextImport = pn.pane.HTML(\n '''\n Water importeren uit WAZ Getelo, NVB Nordhorn en Haaksbergen. Import van 4,5 Mm\\u00b3/jr ↴''', width=300, align="start", styles={}\n)\n\ntextSmartM = pn.pane.HTML('''\n Gebruik van slimme meters thuis, vermindering van 5% van het verbruik ↴''', width=300, align="start", styles={}\n)\n\ntextIndustrial = pn.pane.HTML(\n '''Voeg ongebruikt water uit industri\xeble vergunningen toe. Voeg 1,66 Mm\xb3/jr toe ↴\n''', width=300, align="start"\n)\n\ntextEnd = pn.pane.HTML(\n '''
\n ''', width=300, align="start", styles={}\n)\n\ntextDivider0 = pn.pane.HTML('''
''')\ntextDivider1 = pn.pane.HTML('''
''')\ntextDivider2 = pn.pane.HTML('''
''')\n\nfile_create = pn.widgets.Button(name='Create Report', button_type='primary', width=300, margin=10,)\n\nfile_download = pn.widgets.FileDownload(file="Vitalens_report.pdf", button_type="primary" , width=300, margin=10,)\n\n# Create a spinner\nspinner = pn.indicators.LoadingSpinner(width=30, height=30, value=False)\n\ndef spacer(size):\n spacerVertical = pn.Spacer(height=size)\n return spacerVertical\n\ndisclaimer = pn.pane.HTML('''
\n
\n

Welkom bij de Vitalens App

\n

\n Deze app helpt je om waterputten in de regio Overijssel Zuid te beheren en te analyseren. Het stelt gebruikers in staat om de capaciteit van de putten, kosten, milieu-impact en andere belangrijke factoren voor de planning van watervoorzieningen bij te houden.\n

\n\n

Belangrijkste functies

\n
    \n
  • Live Datavisualisatie: Bekijk en werk samen met putlocaties, extractieniveaus en milieugrenzen.
  • \n
  • Scenarioanalyse: Simuleer verschillende vraagscenario's voor water, zoals bevolkingsgroei of de behoeften van kleine bedrijven, om te zien hoe deze de watervoorziening en kosten kunnen be\xefnvloeden.
  • \n
  • Milieukostenramingen: Bereken milieukosten zoals CO2-uitstoot en de effecten van droogte voor elke put, en bekijk de beperkingen voor beschermde gebieden zoals Natura2000.
  • \n
  • Aangepast putbeheer: Verander de extractieniveaus en status (actief of inactief) van putten om watergebruik en effici\xebntie te optimaliseren.
  • \n
  • Interactieve data-exploratie: Verken gedetailleerde informatie over putten, waaronder leveringszekerheid, operationele kosten, milieu-impact en prestaties per gebied.
  • \n
\n\n

Disclaimer

\n

\n Deze app biedt nuttige inzichten en visualisaties voor het beheer van water, maar is gebaseerd op schattingen en aannames. De daadwerkelijke prestaties van putten, milieu-impact en kosten kunnen vari\xebren door factoren in de echte wereld, zoals veranderende omstandigheden of nieuwe regelgeving.\n

\n

\n Let op: De resultaten van de app zijn alleen bedoeld als richtlijn en zijn mogelijk niet exact. Voor kritieke beslissingen, raadpleeg lokale experts en gebruik geverifieerde gegevens.\n

\n\n

\n \xa9 2024 Vitalens App. Vitens en Universiteit Twente. Alle rechten voorbehouden.\n

\n
\n
\n\n\n \n ''', width=700, max_height=800)\n\nflaotingDisclaimer = pn.layout.FloatPanel(disclaimer, name= "Welcome", margin=20, contained=False, position="center") \n\n\n\nscenario_layout = pn.Column(textScenarioPop, Scenario_Button, textDivider3, ScenarioSmall_Button, textEnd, ButtonReset, width=320)\n\nSupply_measures_layout = pn.Column(textMeasureSupp, ButtonSmallWells,textCloseNatura, ButtonCloseNatura, textImport, ButtonImportWater, textIndustrial, ButtonAddExtraIndustrial, textEnd, ButtonReset, width=320)\n\nDemand_measures_layout = pn.Column(textMeasureDemand, ButtonDemand, textDivider0, textSmartM, ButtonSmartMeter, textEnd, ButtonReset, width = 320)\n\nfirstColumn = pn.Column(balance_area_Text,all_wellsButton, radioButton_layout)\nsecondColumn = pn.Column(file_create, spinner, file_download)\n\n\n\n\ntabTop = pn.Tabs(("1. Scenario's", scenario_layout), ("2. Aanbod", Supply_measures_layout), ("3. Vraag", Demand_measures_layout), width = 320)\ntabBottom = pn.Tabs(("4. Putcapaciteiten", firstColumn), ("5. Rapport genereren", secondColumn), width = 320)\n\ntabs = pn.Column(tabTop, tabBottom, sizing_mode="scale_height")\n\n# MAIN WINDOW\n\n# map_pane = pn.pane.HTML(create_map(52.38, 6.7, 10), sizing_mode="stretch_both")\nmap_pane = pn.pane.plot.Folium(update_layers(), sizing_mode="stretch_both")\n\nminusSVG= pn.pane.SVG(' ', max_width=40,sizing_mode='stretch_width', align='center')\n\nequalSVG = pn.pane.SVG(' ', max_width=40,sizing_mode='stretch_width', align='center')\n\ntotal_extraction = pn.indicators.Number(\n name="Totale Levering",\n value=calculate_total_extraction(),\n format="{value:.2f} Mm\\u00b3/jr",\n default_color='#3850a0',\n font_size="20pt",\n title_size="12pt",\n sizing_mode="scale_width",\n align='center',\n colors=[(wells["Extraction_2023__Mm3_per_jr_"].sum() - 0.1, '#D9534F'), (wells["Extraction_2023__Mm3_per_jr_"].sum(), '#3850a0'), (1000, '#92C25B')]\n)\n\ntotal_demand = pn.indicators.Number(\n name="Totale Watervraag",\n value=calculate_total_Demand,\n format="{value:0,.2f} Mm\\u00b3/jr",\n font_size="20pt",\n title_size="12pt",\n default_color='#3850a0',\n sizing_mode="scale_width", align='center',\n colors=[(original_demand - 0.1, '#92C25B'), (original_demand, '#3850a0'), (1000, '#D9534F')]\n)\n\ntotal_difference = pn.indicators.Number(\n name="Waterbalans",\n value=calculate_difference(),\n format="{value:.2f} Mm\\u00b3/jr",\n colors=[(0, '#d9534f'), (10, '#f2bf58'), (100, '#92c25b')],\n default_color='#3850a0',\n font_size="20pt",\n title_size="12pt",\n sizing_mode="scale_width", align='center'\n)\n\ntotal_extraction_TT = pn.widgets.TooltipIcon(value="De totale levering wordt berekend als de som van de volumes grondwater die per locatie in een jaar worden onttrokken. De totale vraag wordt berekend als het jaarlijkse verbruik van drinkwater door inwoners en kleine bedrijven.")\n\ntotal_opex = pn.indicators.Number(\n name="Totale OPEX",\n value=calculate_total_OPEX(),\n format="{value:0,.2f} M\\u20AC/jr",\n default_color='#3850a0',\n font_size="20pt",\n title_size="12pt",\n align="center",\n sizing_mode="stretch_width",\n colors=[(original_OPEX - 0.1, '#92C25B'), (original_OPEX, '#3850a0'), (1000, '#D9534F')]\n)\n\ntotal_opex_TT = pn.widgets.TooltipIcon(value="Totale jaarlijkse operationele uitgaven (OPEX).")\n\ntotal_capex = pn.indicators.Number(\n name="Totale CAPEX",\n value=calculate_total_CAPEX(),\n format="{value:0,.2f} M\\u20AC",\n default_color='#3850a0',\n font_size="20pt",\n title_size="12pt",\n align="center",\n sizing_mode="stretch_width",\n colors=[(0, '#3850a0'), (1000, '#D9534F')]\n)\n\n\ntotal_capex_TT = pn.widgets.TooltipIcon(value="Totale investeringsuitgaven (CAPEX) voor het uitbreiden van de onttrekkingscapaciteit.")\n\n\n# balance_opex = calculate_total_OPEX_by_balance()\n# balance_opex_indicators = {\n# balance: pn.indicators.Number(\n# name=f"OPEX {balance}",\n# value=value,\n# format="{value:0,.2f} M\\u20AC/yr",\n# default_color='#3850a0',\n# font_size="28pt",\n# title_size="18pt",\n# align="center",\n# )\n# for balance, value in balance_opex.items()\n# }\n\nexcess_cap = pn.indicators.Number(\n name="Overcapaciteit",\n value=calculate_available(),\n format="{value:0.2f} Mm\\u00b3/yr",\n default_color='#3850a0',\n font_size="20pt",\n title_size="12pt",\n colors=[(original_excess-0.1, '#D9534F'), (original_excess, '#3850a0'),(1000, '#92C25B')]\n\n)\n\nexcess_cap_TT = pn.widgets.TooltipIcon(value="Jaarlijks beschikbaar water dat niet uit de putten wordt gewonnen en binnen de maximaal toegestane onttrekking valt.")\nexcess_cap_row = pn.Row(excess_cap, excess_cap_TT)\n\nindustrial_extract = pn.indicators.Number(\n name="Industri\xeble Wateronttrekking",\n value=calculate_industrial_extract(),\n format="{value:0.2f} Mm\\u00b3/yr",\n default_color='#3850a0',\n font_size="20pt",\n title_size="12pt",\n)\nindustrial_extract_TT = pn.widgets.TooltipIcon(value="Geschatte jaarlijkse grondwateronttrekking door grote industrie\xebn waar Vitens geen controle over heeft.")\n\n\nindustrial_extract_row = pn.Row(industrial_extract, industrial_extract_TT)\n\n\nright_pane = pn.Column(excess_cap_row,industrial_extract_row)\n\n# own_pane = pn.indicators.Number(\n# name="Landownership",\n# value=calculate_ownership(),\n# format="{value:0.2f} %",\n# default_color='#3850a0',\n# font_size="20pt",\n# title_size="12pt",\n# align="center",\n# colors=[(75, "#F19292"), (85, "#F6D186"), (100, "#CBE2B0")],\n# sizing_mode="stretch_width"\n# )\n\nnatureMidDamage_value = pn.indicators.Number(\n name="Geschatte Gevoelige Natuur getroffen gebied",\n value=calculate_affected_Sensitive_Nature(),\n format="{value:0.2f} Ha",\n default_color='#3850a0',\n font_size="14pt",\n title_size="10pt",\n sizing_mode="stretch_both",\n styles = {\n 'font-family': "Roboto"\n }\n)\n\nnatureHighDamage_value = pn.indicators.Number(\n name="Geschatte Zeer Gevoelige Natuur getroffen gebied",\n value=calculate_affected_VerySensitive_Nature(),\n format="{value:0.2f} Ha",\n default_color='#3850a0',\n font_size="14pt",\n title_size="10pt",\n # sizing_mode="stretch_both",\n styles = {\n 'font-family': "Roboto"\n }\n)\n\nnatureDamage_TT = pn.widgets.TooltipIcon(value='Dit gebied komt overeen met de omvang van droogtegevoelige, grondwaterafhankelijke natuur die mogelijk wordt be\xefnvloed door grondwaterwinning.')\n\n\n# nature_title = pn.Row(natureMidDamage_value,natureDamage_TT, sizing_mode="scale_both" )\n\n# Use pn.bind to dynamically bind the number of stars to the pane\nkeukenhofsMid = pn.bind(generate_area_SVG, natureMidDamage_value)\nkeukenhofsHigh = pn.bind(generate_area_SVG, natureHighDamage_value)\nkeuk_text = pn.pane.HTML("

Weergegeven in aantal stadscentra van Enschede

")\nnatura_pane = pn.Column(natureDamage_TT, natureHighDamage_value, spacer(10), keukenhofsHigh, natureMidDamage_value, spacer(50), keukenhofsMid, keuk_text, sizing_mode='stretch_both')\n\npipes_TT = pn.widgets.TooltipIcon(value="Elk pictogram vertegenwoordigt het aantal verbindingen tussen twee balansgebieden, dit is een indicator van kwetsbaarheid in het systeem.")\n\npipes_pane = pn.Column(\n pipes_TT, \n generate_pipes_SVG("Reggeland", "Stedenband", 1), \n generate_pipes_SVG("Reggeland", "Hof van Twente", 2), \n generate_pipes_SVG("Reggeland", "Dinkelland", 1), \n generate_pipes_SVG("Hof van Twente", "Stedenband", 3), \n generate_pipes_SVG("Dinkelland", "Stedenband", 1), \n width=350\n)\n\nco2_pane = pn.indicators.Number(\n name="CO\\u2082 Emissiekosten",\n value=calculate_total_CO2_cost(),\n format="{value:0,.2f} M\\u20AC/jr",\n default_color='#3850a0',\n font_size="20pt",\n title_size="12pt",\n colors=[(original_CO2 - 0.1, '#92C25B'), (original_CO2, '#3850a0'), (1000, '#D9534F')]\n)\n\ndrought_pane = pn.indicators.Number(\n name="Schadekosten door Droogte",\n value=calculate_total_Drought_cost(),\n format="{value:0,.2f} M\\u20AC/jr",\n default_color='#3850a0',\n font_size="20pt",\n title_size="12pt",\n colors=[(original_Draught - 0.1, '#92C25B'), (original_Draught, '#3850a0'), (1000, '#D9534F')]\n)\n\nlzh = pn.indicators.Gauge(\n name="Algemene LZH",\n value=calculate_lzh(),\n bounds=(0, 150),\n format="{value} %",\n colors=[(0.66, "#D9534F"), (0.8, "#f2bf57"), (0.9, "#92C25B"), (1, "#8DCEC0")],\n custom_opts={\n "pointer": {"interStyle": {"color": "auto"}},\n "detail": {"valueAnimation": True, "color": "inherit"},\n },\n align=("center", 'center'), height=250, title_size=14\n)\n\nlzh.param.watch(update_indicators, "value")\nlzh_definition = pn.pane.HTML("LZH: Het is het percentage van de vraag naar drinkwater dat door de levering wordt gedekt")\nlzh_tooltip = pn.widgets.TooltipIcon(value="LZH: Leveringszekerheid, is het percentage van de vraag naar drinkwater dat door de levering wordt gedekt. Je kunt de LZH voor elk balansgebied zien door de tabbladen aan de rechterkant te selecteren. Deze waarden gaan uit van een gesloten systeem.", width=10, align='end')\n\n# LZH for each balance area - currently not on display\n"""\nbalance_lzh_gauges = {}\nbalance_lzh_values = calculate_lzh_by_balance()\nfor area, value in balance_lzh_values.items():\n gauge = pn.indicators.Gauge(\n name=f"LZH \\n{area}",\n value=value,\n bounds=(0, 630),\n format="{value} %",\n colors=[(0.2, "#D9534F"), (0.24, "#f2bf57"),(0.27, "#92C25B"), (1, "#8DCEC0")],\n custom_opts={\n "pointer": {"interStyle": {"color": "auto"}},\n "detail": {"valueAnimation": True, "color": "inherit"},\n },\n align=("center",'center'), height = 250, title_size = 14\n )\n balance_lzh_gauges[area] = gauge\n"""\n\n\ndef printResults(filename1):\n print("Button clicked, generating report...")\n\n printingReport.styledWells(active_wells_df)\n printingReport.generate_matplotlib_stackbars(active_wells_df, filename1)\n # printingReport.generate_image_fromInd(pane=lzh, filename=filename2)\n printingReport.createPDF(filename1, Scenario_Button, ScenarioSmall_Button, ButtonSmallWells, ButtonCloseNatura, ButtonImportWater, ButtonAddExtraIndustrial, ButtonDemand,total_demand,total_extraction,total_opex,total_capex, co2_pane,drought_pane,natureMidDamage_value, natureHighDamage_value)\n return print("File Created")\n\n# When clicking the button, show the spinner and run the function\ndef on_button_click(event):\n spinner.value = True # Show the spinner\n printResults("wells_Distribution.png")\n spinner.value = False # Hide the spinner when done\n pn.state.notifications.position = 'bottom-left'\n pn.state.notifications.success('Report File created, you can download it now', duration=4000)\n\n\nfile_create.on_click(on_button_click)\n\n\n# lzhTabs = pn.Tabs(lzh, *balance_lzh_gauges.values(), align=("center", "center"))\nEnv_pane = pn.Column(co2_pane, drought_pane)\n\n# indicatorsArea = pn.GridSpec(sizing_mode="scale_both")\n# indicatorsArea = pn.Tabs(lzh, *balance_lzh_gauges.values(), ("Help",lzh_tooltip), align=("center", "center"), sizing_mode="scale_height", tabs_location="right")\n\n\n\n\nCostPane = pn.Row(\n total_opex, total_opex_TT, total_capex, total_capex_TT, align=("center", "center")\n)\n\nverticalLine = pn.pane.HTML(\n '''\n
\n '''\n)\n\nSupp_dem = pn.Row(\n total_extraction, minusSVG, total_demand, equalSVG, total_difference, total_extraction_TT)\n\napp_title = pn.pane.Markdown("## Scenario: State - 2022", styles={\n "text-align": "right",\n "color": "#2f4279"\n})\n\n\n\nMapTitle = pn.pane.HTML('''Overijssel Zuid''')\nMap_help = pn.widgets.TooltipIcon(value="De gegevens die op deze kaart worden weergegeven zijn statisch. Dit betekent dat ze niet veranderen wanneer de widgets aan de linkerkant van de app worden aangepast. Ze vertegenwoordigen bevolkingsgegevens van december 2022 en de waterwinning van 2023.\\n\\nDe Balansgebieden vertegenwoordigen gebieden binnen het Overijssel Zuid Cluster die direct worden gevoed door ten minste een productielocatie en zijn gekoppeld aan een ander balansgebied voor dynamische waterverdeling.", width=10, align='end')\n\n\nMapTitle_TT = pn.Row( Map_help,MapTitle, align="end", sizing_mode="scale_width")\n\nmain1 = pn.GridSpec(sizing_mode="scale_both")\nmain1[0, 1:2] = pn.Column(MapTitle_TT, map_pane)\n\nIndicatorsPane = pn.GridSpec(sizing_mode="stretch_both")\nIndicatorsPane[0,0:2] = pn.Column(\n lzh, lzh_definition, textDivider0, Supp_dem, textDivider1, CostPane, textDivider2, natura_pane,\n scroll=True\n)\nIndicatorsPane[0,2] = pn.Column(\n Env_pane, right_pane, textDivider0, pipes_pane,\n sizing_mode="stretch_both",\n scroll=True\n)\n\nmain1[0, 0] = pn.Column(app_title, IndicatorsPane, sizing_mode="scale_both")\n\n\nBox = pn.template.MaterialTemplate(\n title="Vitalens",\n logo="https://uavonline.nl/wp-content/uploads/2020/11/vitens-logo-1.png",\n sidebar=[tabs],\n main=[main1],\n header_background= '#3850a0',\n header_color= '#f2f2ed',\n sidebar_width = 350,\n collapsed_sidebar = False,\n)\n\nBox.main.append(flaotingDisclaimer)\n\n\n\n\ndef total_extraction_update():\n """\n Update the total extraction and related indicators.\n """\n total_extraction.value = calculate_total_extraction()\n total_opex.value = calculate_total_OPEX()\n # update_balance_opex()\n # update_balance_lzh_gauges()\n update_indicators()\n total_demand.value = calculate_total_Demand()\n total_difference.value = calculate_difference()\n calculate_affected_Sensitive_Nature()\n map_pane\n co2_pane.value = calculate_total_CO2_cost()\n drought_pane.value = calculate_total_Drought_cost()\n flaotingDisclaimer\n\ntotal_extraction_update()\nBox.servable()\n\nawait write_doc()` await pyodide.runPythonAsync(code); } main();